#pragma once
#include "rpc_register.hpp"
#include "rpc_topic.hpp"
#include "rpc_caller.hpp"
#include "../common/net.hpp"
#include "../common/dispatcher.hpp"

namespace qiangsir
{
    namespace client
    {
        class RegistryClient
        {
        public:
            using ptr = std::shared_ptr<RegistryClient>;
            // 构造函数传入注册中心地址信息，用于连接注册信息
            RegistryClient(const std::string &ip, int port)
                : _requestor(std::make_shared<Requestor>()),
                  _provider(std::make_shared<Provider>(_requestor)),
                  _dispatcher(std::make_shared<Dispatcher>())
            {

                auto rsp_cb = std::bind(&Requestor::onResponse, _requestor.get(),
                                        std::placeholders::_1, std::placeholders::_2);

                _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVERICE, rsp_cb);

                auto msg_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), std::placeholders::_1, std::placeholders::_2);

                _client = ClientFactory::create(ip, port);
                _client->setMessageCallBack(msg_cb);
                _client->connect();
            }
            bool serviceRegistry(const std::string &method, const Address &host)
            {
                return _provider->registerMethod(_client->connection(), method, host);
            }

        private:
            Requestor::ptr _requestor;
            BaseClient::ptr _client;
            Provider::ptr _provider;
            Dispatcher::ptr _dispatcher;
        };

        class DiscoveryClient
        {
        public:
            using ptr = std::shared_ptr<DiscoveryClient>;
            // 构造函数传入注册中心地址信息，用于连接注册信息
            DiscoveryClient(const std::string &ip, int port, const Discovery::OffLineCallBack &cb)
                : _requestor(std::make_shared<Requestor>()),
                  _discovery(std::make_shared<Discovery>(_requestor, cb)),
                  _dispatcher(std::make_shared<Dispatcher>())
            {

                auto rsp_cb = std::bind(&Requestor::onResponse, _requestor.get(),
                                        std::placeholders::_1, std::placeholders::_2);

                auto req_cb = std::bind(&Discovery::serviceRequest, _discovery.get(),
                                        std::placeholders::_1, std::placeholders::_2);

                _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVERICE, rsp_cb);
                _dispatcher->registerHandler<ServiceRequest>(MType::REQ_SERVERICE, req_cb);

                auto msg_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(), std::placeholders::_1, std::placeholders::_2);

                _client = ClientFactory::create(ip, port);
                _client->setMessageCallBack(msg_cb);
                _client->connect();
            }
            bool serviceDiscovery(const std::string &method, Address &host)
            {
                return _discovery->serviceDiscover(_client->connection(), method, host);
            }

        private:
            Requestor::ptr _requestor;
            BaseClient::ptr _client;
            Discovery::ptr _discovery;
            Dispatcher::ptr _dispatcher;
        };

        class RpcClient
        {
        public:
            using ptr = std::shared_ptr<RpcClient>;
            // 构造函数传入注册中心地址信息，用于连接注册信息
            RpcClient(bool enablediscovery, const std::string &ip, int port)
                : _enablediscovery(enablediscovery),
                  _requestor(std::make_shared<Requestor>()),
                  _dispatcher(std::make_shared<Dispatcher>()),
                  _caller(std::make_shared<RpcCaller>(_requestor))
            {
                auto rsp_cb = std::bind(&Requestor::onResponse, _requestor.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MType::RSP_RPC, rsp_cb);

                if (_enablediscovery)
                {
                    auto offlinecb = std::bind(&RpcClient::delClient, this, std::placeholders::_1);
                    _discovery_client = std::make_shared<DiscoveryClient>(ip, port, offlinecb);
                }
                else
                {
                    auto msg_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                                            std::placeholders::_1, std::placeholders::_2);
                    _rpc_client = ClientFactory::create(ip, port);
                    _rpc_client->setMessageCallBack(msg_cb);
                    _rpc_client->connect();
                }
            }
            bool call(const std::string &method, const Json::Value &params, Json::Value &result)
            {
                // 1.查询业务提供者  2.获取或新建客户端 3.调用call函数
                BaseClient::ptr client = getClient(method);
                if (client == nullptr)
                {
                    return false;
                }
                return _caller->call(client->connection(), method, params, result);
            }
            bool call(const std::string &method, const Json::Value &params, std::future<Json::Value> &result)
            {
                // 1.查询业务提供者  2.获取或新建客户端 3.调用call函数
                BaseClient::ptr client = getClient(method);
                if (client == nullptr)
                {
                    return false;
                }
                return _caller->call(client->connection(), method, params, result);
            }
            bool call(const std::string &method, const Json::Value &params, const RpcCaller::AsyncJsonCallBack &cb)
            {
                // 1.查询业务提供者  2.获取或新建客户端 3.调用call函数
                BaseClient::ptr client = getClient(method);
                if (client == nullptr)
                {
                    return false;
                }
                return _caller->call(client->connection(), method, params, cb);
            }

        private:
            struct AddressHash
            {
                size_t operator()(const Address &addr) const
                {
                    std::string host = addr.first + std::to_string(addr.second);
                    return std::hash<std::string>{}(host);
                }
            };
            BaseClient::ptr getClient(const std::string &method)
            {
                BaseClient::ptr client;
                if (_enablediscovery)
                {
                    Address host;
                    bool ret = _discovery_client->serviceDiscovery(method, host);
                    if (ret == false)
                    {
                        ELOG("当前 %s 方法未找到提供者", method.c_str());
                        return BaseClient::ptr();
                    }
                    client = getClient(host);
                    if (client.get() == nullptr)
                    {
                        client = newClient(host);
                    }
                }
                else
                {
                    client = _rpc_client;
                }
                return client;
            }
            BaseClient::ptr getClient(const Address &host)
            {
                std::unique_lock<std::mutex> mutex(_mutex);
                BaseClient::ptr client;
                auto it = _clients.find(host);
                if (it == _clients.end())
                {
                    return BaseClient::ptr();
                }
                return it->second;
            }
            BaseClient::ptr newClient(const Address &host)
            {
                std::unique_lock<std::mutex> mutex(_mutex);
                BaseClient::ptr client;
                auto msg_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                client = ClientFactory::create(host.first, host.second);
                client->setMessageCallBack(msg_cb);
                client->connect();
                _clients.insert(std::make_pair(host, client));
                return client;
            }
            void delClient(const Address &host)
            {
                std::unique_lock<std::mutex> mutex(_mutex);
                _clients.erase(host);
            }

        private:
            bool _enablediscovery;
            Requestor::ptr _requestor;
            DiscoveryClient::ptr _discovery_client;
            RpcCaller::ptr _caller;
            Dispatcher::ptr _dispatcher;
            BaseClient::ptr _rpc_client;
            std::mutex _mutex;
            std::unordered_map<Address, BaseClient::ptr, AddressHash> _clients;
        };

        class TopicClient
        {
        public:
            using ptr = std::shared_ptr<TopicClient>;
            // 构造函数传入注册中心地址信息，用于连接注册信息
            TopicClient(const std::string &ip, int port)
                : _requestor(std::make_shared<Requestor>()),
                  _dispatcher(std::make_shared<Dispatcher>()),
                  _topic(std::make_shared<TopicManange>(_requestor))
            {
                auto rsp_cb = std::bind(&Requestor::onResponse, _requestor.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MType::RSP_TOPIC, rsp_cb);

                auto top_cb = std::bind(&TopicManange::onPublish, _topic.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<TopicRequest>(MType::REQ_TOPIC, top_cb);

                auto msg_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _client = ClientFactory::create(ip, port);
                _client->setMessageCallBack(msg_cb);
                _client->connect();
            }
            bool create(const std::string &key)
            {
                return _topic->create(_client->connection(), key);
            }
            bool remove(const std::string &key)
            {
                return _topic->remove(_client->connection(), key);
            }
            bool subsribe(const std::string &key, const TopicManange::SubCallBack &cb)
            {
                return _topic->subsribe(_client->connection(), key, cb);
            }
            bool cancel(const std::string &key)
            {
                return _topic->cancel(_client->connection(), key);
            }
            bool publish(const std::string &key, const std::string &msg)
            {
                return _topic->publish(_client->connection(), key, msg);
            }
            void onShutdown()
            {
                _client->shutDown();
            }

        private:
            Requestor::ptr _requestor;
            TopicManange::ptr _topic;
            Dispatcher::ptr _dispatcher;
            BaseClient::ptr _client;
        };
    }
}